from niScope.types import *
import ctypes
import ctypes.util
import numpy
import os
import sys
import textwrap
import warnings

class Scope():
    if os.name == 'nt':
        libName = ctypes.util.find_library("niScope_32")
        libniScope = ctypes.windll.LoadLibrary(libName)

    def __init__(self, resourceName="Dev5", IDQuery=False, resetDevice=False):
        
        self.info = wfmInfo()
        self.vi = ViSession()
        status = self.CALL('init', 
                           ViRsrc(resourceName),
                           ViBoolean(IDQuery), 
                           ViBoolean(resetDevice), 
                           ctypes.byref(self.vi))
        return None
        
    def ConfigureHorizontalTiming(self,
    minSampleRate=20000000,
    minNumPts=1000,
    refPositioin=0.5,
    numRecords=1,
    enforceRealtime=True):
        status = self.CALL('ConfigureHorizontalTiming', self.vi,
            ViReal64(minSampleRate),
            ViInt32(minNumPts),
            ViReal64(refPositioin),
            ViInt32(numRecords),
            ViBoolean(enforceRealtime))
        return 
        
    def CALL(self, name, *args):
        """
        Calls libniScope function "name" and arguments "args".
        """
        funcname = 'niScope_' + name
        func = getattr(libniScope, funcname)
        new_args = []
        for a in args:
            if isinstance (a, unicode):
                print name, 'argument', a, 'is unicode'
                new_args.append (str (a))
            else:
                new_args.append (a)
        status = func(*new_args)
        if status is not 0:
            print "Error in: "+name
            self.errorHandler(status)
            self.error_message(status)
        return status
        
        
    def ConfigureVertical(self,
        channelList="0",
        range=10,
        offset=0,
        coupling=COUPLING.DC,
        probeAttenuation=1,
        enabled=True):
        """
        dfs
        """
        status = self.CALL("ConfigureVertical", self.vi,
            ViConstString(channelList),
            ViReal64(range),
            ViReal64(offset),
            ViInt32(coupling),
            ViReal64(probeAttenuation),
            ViBoolean(enabled))
        return 
    
    def ConfigureTrigger(self, trigger_type='Immediate', *settings):
        """
        Configures scope trigger type and settings. For each trigger type 
        distinct settings must be defined.
        
        Parameter        Valid Values
        
        trigger_type    'Edge'
                        'Hysteresis'
                        'Window'
                        'Window'
                        'Software'
                        'Immediate'
                        'Digital'
                        'Video'
    
    
        Trigger Type    Settings        Default Value    
        
        'Immediate'        N/A
        
        'Edge'          triggerSource     TRIGGER_SOURCE.EXTERNAL
                        level             0
                        slope             SLOPE.POSITIVE
                        triggerCoupling COUPLING.DC
                        holdoff         0
                        delay             0
        
        'Hysteresis'    triggerSource  '0'
                        level              0    
                        hysteresis        0.05
                        slope            SLOPE.POSITIVE
                        triggerCoupling    COUPLING.DC
                        holdoff            0
                        delay            0
            
        'Window'        triggerSource     '0'                   
                        lowLevel        0                       
                        highLevel        0.1                       
                        windowMode        TRIGGER_WINDOW.ENTERING_WINDOW    
                        triggerCoupling    COUPLING.DC             
                        holdoff            0                           
                        delay            0        
                
        'Software'      holdoff            0
                        delay            0    
        
        'Digital'       triggerSource    '0'
                        slope            SLOPE.POSITIVE
                        holdoff            0
                        delay             0
        
        'Video'         triggerSource    '0'                    
                        enableDCRestore False                
                        signalFormat    TV_TRIGGER_SIGNAL_FORMAT.VAL_PAL                
                        event            TV_TRIGGER_EVENT.FIELD1    
                        lineNumber        0                    
                        polarity        TV_TRIGGER_POLARITY.TV_POSITIVE        
                        triggerCoupling    COUPLING.DC                
                        holdoff            0                    
                        delay           0

        """
        args = {
            'Edge':lambda 
                triggerSource      = TRIGGER_SOURCE.EXTERNAL ,
                level              = 0                       ,
                slope              = SLOPE.POSITIVE          ,
                triggerCoupling = COUPLING.DC     ,
                holdoff          = 0                       ,
                delay              = 0
                :(
                    ViConstString    (triggerSource),
                    ViReal64        (level),
                    ViInt32            (slope),
                    ViInt32            (triggerCoupling),
                    ViReal64        (holdoff),
                    ViReal64        (delay)
                ),
            'Hysteresis':lambda
                triggerSource = '0'                 ,
                level               = 0                      ,
                hysteresis         = 0.05                ,
                slope             = SLOPE.POSITIVE      ,
                triggerCoupling = COUPLING.DC ,
                holdoff             = 0                   ,
                delay             = 0
                :(
                    ViConstString     (triggerSource),
                    ViReal64         (level),
                    ViReal64         (hysteresis),
                    ViInt32         (slope),
                    ViInt32         (triggerCoupling),
                    ViReal64         (holdoff),
                    ViReal64         (delay)
                ),
            'Window':lambda
                triggerSource      = '0'                   ,
                lowLevel         = 0                       ,
                highLevel         = 0.1                       ,
                windowMode         = TRIGGER_WINDOW.ENTERING_WINDOW    ,
                triggerCoupling     = COUPLING.DC             ,
                holdoff             = 0                           ,
                delay             = 0        
                :(
                    ViConstString     (triggerSource),
                    ViReal64         (lowLevel),
                    ViReal64         (highLevel),
                    ViInt32         (windowMode),
                    ViInt32         (triggerCoupling),
                    ViReal64         (holdoff),
                    ViReal64         (delay)
                ),
            'Software':lambda
                holdoff     = 0,
                delay     = 0    
                :(
                    ViReal64         (holdoff),
                    ViReal64         (delay)
                ),
            'Immediate':lambda:(),
            'Digital':lambda
                triggerSource     = '0'           ,
                slope             = SLOPE.POSITIVE,
                holdoff             = 0             ,
                delay              = 0
                :(
                    ViConstString     (triggerSource),
                    ViInt32         (slope),
                    ViReal64         (holdoff),
                    ViReal64         (delay)
                ),
            'Video':lambda
            triggerSource     = '0'                                      ,
            enableDCRestore = False                                      ,
            signalFormat     = TV_TRIGGER_SIGNAL_FORMAT.VAL_PAL        ,
            event             = TV_TRIGGER_EVENT.FIELD1    ,
            lineNumber         = 0                                          ,
            polarity         = TV_TRIGGER_POLARITY.POSITIVE        ,
            triggerCoupling     = COUPLING.DC                              ,
            holdoff             = 0                                          ,
            delay = 0
            :(
                ViConstString                     (triggerSource),
                ViBoolean                         (enableDCestore),
                ViInt32                             (signalFormat),
                ViInt32                         (event),
                ViInt32                         (lineNumber),
                ViInt32                         (polarity),
                ViInt32                         (triggerCoupling),
                ViReal64                         (holdoff),     
                ViReal64                         (delay)
            ),
                
        }[trigger_type](*settings)
        status = self.CALL("ConfigureTrigger" + trigger_type, self.vi, *args)
        
    def InitiateAcquisition(self):
        status = self.CALL("InitiateAcquisition", self.vi)
        
    def Abort(self):
        status = self.CALL("Abort", self.vi)
        
    def AcquisitionStatus(self):
        status = self.CALL("AcquisitionStatus", self.vi, ctypes.byref(ViInt32(acq_status)))
        return acq_status.value
        
    def Commit(self):
        return self.CALL("Commit", self.vi)
    
    def Fetch(self,data,channel="0",timeout=1):
        data_type = {
            'float64' :''            ,
            'int8'    :'Binary8'  ,
            'int16'   :'Binary16' ,
            'int32'   :'Binary32' }[data.dtype.name]
        numSamples = max(data.shape)
        status = self.CALL("Fetch" + data_type, self.vi,
            ViConstString(channel),
            ViReal64(timeout),
            ViInt32(numSamples),
            data.ctypes.data,
            ctypes.byref(self.info)
            )
        return data
            
    def read(self):
        self.ConfigureHorizontalTiming()
        self.ConfigureVertical()
        self.ConfigureTrigger()
        self.InitiateAcquisition()
        data = self.Fetch()
        self.close()
        return data
    
    def close(self):
        status = self.CALL("close", self.vi)

# not implemented
#ViStatus _VI_FUNC niScope_FetchComplex ( ViSession vi,
#                                         ViConstString channelList,
#                                         ViReal64 timeout,
#                                         ViInt32 numSamples,
#                                         NIComplexNumber* wfm,
#                                         struct niScope_wfmInfo* wfmInfo);
#
#                                         
# ViStatus _VI_FUNC niScope_FetchComplexBinary16 ( ViSession vi,
                                                 # ViConstString channelList,
                                                 # ViReal64 timeout,
                                                 # ViInt32 numSamples,
                                                 # NIComplexI16* wfm,
                                                 # struct niScope_wfmInfo* wfmInfo);



    def errorHandler (self, errorCode):
        MAX_FUNCTION_NAME_SIZE      = 55
        IVI_MAX_MESSAGE_LEN = 255
        IVI_MAX_MESSAGE_BUF_SIZE = IVI_MAX_MESSAGE_LEN + 1
        MAX_ERROR_DESCRIPTION = (IVI_MAX_MESSAGE_BUF_SIZE * 2 + MAX_FUNCTION_NAME_SIZE + 75)

        errorSource          = ctypes.create_string_buffer(MAX_FUNCTION_NAME_SIZE)
        errorDescription     = ctypes.create_string_buffer(MAX_ERROR_DESCRIPTION)
        self.CALL("errorHandler", self.vi, ViInt32(errorCode), errorSource, errorDescription)
        print errorSource.value
        print errorDescription.value
    
    def error_message (self, errorCode):
        IVI_MAX_MESSAGE_LEN = 255
        IVI_MAX_MESSAGE_BUF_SIZE = IVI_MAX_MESSAGE_LEN + 1
        errorMessage     =     ctypes.create_string_buffer(IVI_MAX_MESSAGE_BUF_SIZE)
        self.CALL("error_message", self.vi, ViStatus(errorCode), errorMessage)
        print errorMessage.value

    def GetAttribute(self,DataType,channel,attrID,value):
        ConvertToDataType = getattr(niScope.types,DataType)
        self.CALL("GetAttribute"+DataType,
                  self.vi,
                  ViConstString(channel),
                  ViAttr(attrID),
                  ConvertToDataType(value))

    def SetAttribute(self,DataType,channel,attrID,value): 
        ConvertToDataType = getattr(niScope.types,DataType)
        self.CALL("SetAttribute"+DataType,
                  self.vi,
                  ViConstString(channel),
                  ViAttr(attrID),
                  ConvertToDataType(value))
            
    def CheckAttribute(self,DataType,channel,attrID,value):
        ConvertToDataType = getattr(niScope.types,DataType)
        self.CALL("CheckAttribute"+DataType,
                  self.vi,
                  ViConstString(channel),
                  ViAttr(attrID),
                  ConvertToDataType(value))   
        


    
